; 实现7.2.2小节的2．中四则运算例op（里面调用了若干CMD程序中的辅助例程）
org 100h 		; 可编译成COM文件
	; 置段寄存器DS、ES、SS = CS
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	; 置栈顶指针SP = 100H -4
	mov sp, 100h-4
	
	; 显示x和y的值
	call newline	; 回车换行
	; 显示"x = "
	mov bp, xstr	; BP = 串地址
	mov cx, xstrlen; CX = 串长
	call DispStr	; 调用显示字符串例程
	; 显示x的值
	mov ax, [x]	; AX = x
	call GetDigStr	; 获取AX的十进制数字串
	call DispStr	; 调用显示字符串例程
	call newline	; 回车换行
	; 显示"y = "
	mov bp, ystr	; BP = 串地址
	mov cx, ystrlen; CX = 串长
	call DispStr	; 调用显示字符串例程
	; 显示x的值
	mov ax, [y]	; AX = y
	call GetDigStr	; 获取AX的十进制数字串
	call DispStr	; 调用显示字符串例程
	call newline	; 回车换行
	
again: ; 循环开始
	; 显示输入提示串
	call newline	; 回车换行
	mov bp, pstr	; BP = 串地址
	mov cx, pstrlen; CX = 串长
	call DispStr	; 调用显示字符串例程
	call newline	; 回车换行
	; 读按键（返回的按键ASCII码在AL中）
	mov ah, 0 	; 功能号
	int 16h 		; 调用16H号中断
	mov cl, al		; CL = AL
	; 计算x op y --------------------------------
	mov ax, [x]	; AX = x
	mov bx, [y]	; BX = y
	; x+y
	cmp cl, '+'	; CL = '+' ?
	jne .1		; 不等跳转到.1
	add ax, bx	; AX += BX
	jmp .6		; 跳转到.6
.1: ; x-y
	cmp cl, '-'		; CL = '-' ?
	jne .2		; 不等跳转到.2
	sub ax, bx	; AX -= BX
	jmp .6		; 跳转到.6
.2: ; x*y
	cmp cl, '*'	; CL = '*' ?
	jne .3		; 不等跳转到.3
	mul bx		; AX *= BX
	jmp .6		; 跳转到.6
.3: ; x/y
	cmp cl, '/'		; CL = '/' ?
	jne .4		; 不等跳转到.4
	mov dx, 0	; DX = 0
	div bx		; DX:AX /= BX
	jmp .6		; 跳转到.6
.4: ; ESC退出
	cmp cl, 1bh	; CL = ESC ?
	jne .5		; 不等跳转到.5
	; 退出程序
	mov ax, 4c00h	; 功能号AH = 4Ch，返回值AL = 0
	int 21h		; 21h号DOS系统调用中断
.5: ; 显示出错信息
	mov bp, wstr	; BP = 串地址
	mov cx, wstrlen; CX = 串长
	call DispStr	; 调用显示字符串例程
	call newline	; 回车换行	
	jmp again		; 重新输入运算符
.6: ; 显示计算结果
	; 显示"x op y = "
	push ax		; 保存计算结果AX进栈
	mov [rstr + 2], cl ; 在串"x ? y = "中填入运算符
	mov bp, rstr	; BP = 串地址
	mov cx, rstrlen; CX = 串长
	call DispStr	; 调用显示字符串例程
	pop ax		; 从栈中恢复AX
	; 显示计算的结果值
	call GetDigStr	; 获取结果的十进制数字串
	call DispStr	; 调用显示字符串例程
	call newline	; 回车换行	
	; 继续计算
	jmp again

; 定义变量：
x dw 15
y dw 7
xstr db 'x = '
xstrlen equ $ - xstr
ystr db 'y = '
ystrlen equ $ - ystr
pstr db 'Press operator key:(ESC to exit)'
pstrlen equ $ - pstr
wstr db 'Wrong operator!'
wstrlen equ $ - wstr
rstr db 'x   y = '
rstrlen equ $ - rstr

; -------------------------------------------------------------------
newline: ; 换行（显示回车符和换行符）
	; 显示回车符CR（置当前列号=0）
	mov ah, 0Eh 	; 功能号
	mov al, 0Dh 	; 设置AL为回车符CR（ASCII码为0DH）
	mov bl, 0 		; 对文本方式置0
	int 10h 		; 调用10H号显示中断
	; 显示换行符（当前行号++）
	mov ah, 0Eh 	; 功能号
	mov al, 0Ah 	; 设置AL为换行符LF（ASCII码为0AH）
	mov bl, 0 		; 对文本方式置0
	int 10h 		; 调用10H号显示中断
	ret				; 从例程返回
	
; --------------------------------------------------------------------
DispStr: ; 显示字符串例程（需先置串长CX和串地址BP）
	; 获取当前光标位置（返回的行列号分别在DH和DL中）
	push cx			; 保护CX（进栈）
	mov ah, 3		; 功能号
	mov bh, 0		; 第0页
	int 10h 		; 调用10H号显示中断
	pop cx			; 恢复CX（出栈）

	; 在当前位置显示字符串（串长CX和串地址BP已预先设置好了）
	mov ah, 13h		; BIOS中断的功能号（显示字符串）
	mov al, 1 		; 光标放到串尾
	mov bh, 0 		; 页号=0
	mov bl, 0fh		; 字符颜色=不闪（0）黑底（000）亮白字（1111）
	int 10h 		; 调用10H号显示中断
	ret				; 从例程返回
	
; --------------------------------------------------------------------
; 获取字数据值十进制串例程
dn equ 5 ; 最大位数
GetDigStr: ; 以AX为传递参数，[串地址]BP和[字符个数]CX为返回值
	mov cx, 1		; 位数=1（初值）
	mov bp, sbuf	; BP = sbuf + dn - 1 = sbuf的当前位置
	add bp, dn - 1
	mov bx,10		; 除数=10
DLoop: ; 循环开始处
	mov dx, 0		; DX=0, DX:AX / BX -> 商AX、余DX
	div bx
	add dl, 30h		; 余数 + 30h = 对应的数字符ASCII码
	mov [bp], dl	; sbuf[BP] = DL
	cmp ax, 0		; 商AX = 0 ?
	je OutLoop		; = 0 跳出循环
	inc cx			; 位数CX++
	dec bp			; 数字符的当前位置BP--
	jmp DLoop		; 继续循环
OutLoop: ; 退出循环
	ret				; 从例程返回

sbuf: db dn ; 用于存放十进制数字串的缓冲区，大小 = 常量dn（=5）
